#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/errno.h>
#include <linux/uaccess.h>

#include <linux/gpio.h>

#ifdef S3C2440
#include <mach/gpio-samsung.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>
#include <plat/samsung-time.h>
#define PWM_BUZZER SAMSUNG_PWM0
#define BEEP_GPIO S3C2410_GPB(0)
#else
#include <mach/soc.h>
#include <cfg_type.h>
#define BEEP_GPIO (PAD_GPIO_C + 14)
#define GPIOC14_TOUT2 2
#define PWM_BUZZER 2
#endif

#include <linux/pwm.h>

#define NS_IN_1S	1000000000

static dev_t  buz_dev_id;
static struct cdev buz_cdev;
static struct class *buz_class;
static struct device *buz_device;
static struct pwm_device *buzzer_pwm;

static ssize_t buzzer_write(struct file *filp, const char __user *buf, size_t buf_size, loff_t *offset)
{
	int ret = 0;
	unsigned int period, duty;
	unsigned int vol, hz;
	char cmd[64] = {0};
	unsigned int i;

	ret = copy_from_user(cmd, buf, buf_size);
	if (ret < 0) {
		ret = -EFAULT;
		goto err_0;
	}

	hz = 0;
	vol = 0;
	for (i = 0; i < buf_size; i++) {
		if ((cmd[i] >= '0') && (cmd[i] <= '9')) {
			hz *= 10;
			hz += cmd[i] - '0';
		}

		if (cmd[i] == ',') {
			i++;
			break;
		}
	}

	while (i < buf_size) {
		if ((cmd[i] >= '0') && (cmd[i] <= '9')) {
			vol *= 10;
			vol += cmd[i] - '0';
		}
		i++;
	}

	if (hz != 0) {
		period = NS_IN_1S / hz;
	} else {
		period = 0;
	}

	if (vol != 0) {
		duty = period / 100 * vol;
	} else {
		duty = 0;
	}

	printk("<1>hz=%d, vol=%d%%, duty=%d, period=%d\n", hz, vol, duty, period);
	if (hz == 0 || vol == 0) {
		gpio_direction_output(BEEP_GPIO, 0);
	}
	pwm_config(buzzer_pwm, duty, period);

	ret = buf_size;

err_0:
	return ret;
}

static struct file_operations buz_file_ops = {
	.write = buzzer_write,
};

static int __init buzzer_init(void)
{
	int ret;

	ret = alloc_chrdev_region(&buz_dev_id, 4, 1, "buzzer");
	if (ret < 0) {
		printk("regist device id faibuz.\n");
		goto err_1;
	}

	cdev_init(&buz_cdev, &buz_file_ops);
	ret = cdev_add(&buz_cdev, buz_dev_id, 1);
	if (ret < 0) {
		printk("cdev add faibuz.\n");
		goto err_2;
	}

	buz_class = class_create(THIS_MODULE, "buzzer");
	buz_device = device_create(buz_class, NULL, buz_dev_id, NULL, "buzzer");

	ret = gpio_request(BEEP_GPIO, "Buzzer_io");
#ifdef S3C2440
	s3c_gpio_cfgpin(BEEP_GPIO, S3C2410_GPB0_TOUT0);
#else
	nxp_soc_gpio_set_io_func(BEEP_GPIO, GPIOC14_TOUT2);
#endif
	buzzer_pwm = pwm_request(PWM_BUZZER, "buzzer");
	if (buzzer_pwm == NULL) {
		printk("pwm request error.\n");
	}

	pwm_enable(buzzer_pwm);

err_2:
	unregister_chrdev_region(buz_dev_id, 1);	
err_1:
	return ret;
}

static void __exit buzzer_exit(void)
{
	pwm_disable(buzzer_pwm);
	pwm_free(buzzer_pwm);
	gpio_direction_output(BEEP_GPIO, 0);
	gpio_free(BEEP_GPIO);
	device_destroy(buz_class, buz_dev_id);
	class_destroy(buz_class);
	cdev_del(&buz_cdev);
	unregister_chrdev_region(buz_dev_id, 1);
}

module_init(buzzer_init);
module_exit(buzzer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YYW");
